package handle

import (
	"server/game/cache"
	"server/game/itypes"
	"server/game/model"
	"server/share/config"

	"github.com/hwcer/cosgo/registry"
	"github.com/hwcer/cosgo/values"
	"github.com/hwcer/cosmo/update"
	"github.com/hwcer/yyds/context"
	"github.com/hwcer/yyds/errors"
	"github.com/hwcer/yyds/options"
	"github.com/hwcer/yyds/players/verify"
)

func init() {
	Register(&Order{})
}

type Order struct {
}

func (this *Order) Caller(node *registry.Node, handle *context.Context) interface{} {
	f := node.Method().(func(*Order, *context.Context) interface{})
	return f(this, handle)
}

type OrderReply struct {
	Id       string            `json:"id,omitempty" bson:"_id"`          //游戏内部订单ID ,仅仅创建、拉起订单时存在,可用作拉起订单是透传参数或者平台的 外部订单号OutTradeNo
	Trade    string            `json:"trade,omitempty" bson:"trade"`     //流水号，平台订单ID
	Attach   string            `json:"attach,omitempty" bson:"attach"`   //微信等用于拉起客户端充值的代码 ,仅仅创建、拉起订单 新订单 时存在
	Status   model.OrderStatus `json:"status" bson:"status"`             //订单状态
	Receive  float32           `json:"receive,omitempty" bson:"receive"` //实际支付
	Currency string            `json:"cyt,omitempty" bson:"cyt"`         //实际支付代码
}

type OrderCreateArgs struct {
	IID      int32   `json:"iid"`
	Uid      string  `json:"uid" `
	Sid      int32   `json:"sid"`
	Guid     string  `json:"guid"`
	Code     string  `json:"code" `   //平台商店代码
	Branch   string  `json:"branch" ` //支付平台
	Openid   string  `json:"openid" ` //订单创建时间
	Amount   float32 `json:"amount" ` //订单金额(元，默认货币)
	Subject  string  `json:"subject" `
	ClientIp string  `json:"client_ip" `
	Currency string  `json:"cyt" ` //平台对账默认币种
}

// Create 创建订单
// iid int 配置 id
// plat int 支付平台
// openid string 支付账号，目前仅支付宝JSAPI 用到，支付宝支付账号
// 订单必须先创建，用来检查限购情况
func (this *Order) Create(c *context.Context) interface{} {
	iid := c.GetInt32("iid")
	plat := c.GetInt32("plat")
	openid := c.GetString("openid")
	if iid == 0 || plat == 0 {
		return errors.ErrArgEmpty
	}

	orderConfig := config.Data.Order[iid]
	if orderConfig == nil {
		return errors.ErrConfigEmpty
	}

	if orderConfig.Status == 0 {
		return c.Errorf(0, "viper status limit:%v", orderConfig.Status)
	}

	platform := config.Data.Platform[plat]
	if platform == nil {
		return c.Error("pfm not exist")
	}
	currency := config.Data.Currency[platform.Cyt]
	if currency == nil {
		return c.Errorf(0, "currency not exist:%v", platform.Cyt)
	}

	payment := config.GetPayment(iid, plat)
	if payment == nil {
		return c.Errorf(0, "payment empty, key:%v pfm:%v", iid, plat)
	}
	c.Player.Emit(config.Data.Events.OrderVerify, orderConfig.Id)
	if c.Player.Updater.Error != nil {
		return c.Player.Updater.Error
	}
	//开放周期
	if _, err := c.Player.Times.Verify(orderConfig.Times); err != nil {
		return err
	}
	//限制条件
	if len(orderConfig.Condition) > 0 {
		target := verify.Condition(orderConfig.Condition)
		c.Player.Verify.Auto(target)
		c.Player.Verify.Target(target)
		if err := c.Player.Verify.Verify(target); err != nil {
			return err
		}
	}

	data := &model.Order{}
	//购买限制
	if orderConfig.Limit[0] > 0 {
		st, err := c.Player.Times.Start(int64(orderConfig.Limit[0]), 1)
		if err != nil {
			return err
		}
		var n int32
		tx := db.Model(data).Where("uid = ?", c.Uid()).Where("iid = ?", iid)
		tx = tx.Where("create >= ?", st)
		tx = tx.Where("status IN ?", []any{model.OrderStatusSUCCESS, model.OrderStatusPAYMENT})
		tx = tx.Order("update", -1)
		if tx = tx.Count(&n); tx.Error != nil {
			return err
		}
		if n >= orderConfig.Limit[1] {
			return c.Errorf(0, "order limit:%v", n)
		}
	}
	//拉起未完成
	tx := db.Model(data).Where("uid = ?", c.Uid()).Where("iid = ?", iid)
	tx = tx.Order("update", -1)
	tx = tx.Where("status IN ?", []any{model.OrderStatusCREATE, model.OrderStatusPAYMENT})
	tx = tx.Limit(1)
	if tx = tx.Find(data); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected > 0 {
		//已经支付通知客户端可以发货
		if data.Status != model.OrderStatusCREATE {
			return &OrderReply{
				Id:       data.OID,
				Trade:    data.Trade,
				Status:   data.Status,
				Receive:  data.Receive,
				Currency: data.Currency,
			}
		}
		// 新鲜的订单,直接刷新当前订单状态
		if r, err := this.restoreOrder(c, data, platform.Branch, openid); err == nil {
			return r
		} else {
			// 刷新订单状态出错，需要重新下单。。。本来就是下单，放弃续杯
		}
	}
	//正常下单
	role := itypes.Role.All(c.Player.Updater)
	args := OrderCreateArgs{}
	args.IID = iid
	args.Sid = options.Game.Sid
	args.Uid = role.Uid
	args.Guid = role.Guid
	args.Code = payment.Code
	args.Branch = platform.Branch
	args.Openid = openid
	args.Amount = payment.Val
	args.Subject = orderConfig.Name
	args.ClientIp = c.Player.ClientIp
	args.Currency = currency.Key

	reply := &OrderReply{}
	if err := options.Master.Post(options.MasterApiTypeOrderCreate, &args, reply); err != nil {
		return err
	}
	if reply.Id == "" {
		return c.Error("平台返回的订单ID为空")
	}
	now := c.Unix()
	data = &model.Order{
		OID:     reply.Id,
		IID:     iid,
		Uid:     role.Uid,
		Code:    payment.Code,
		Trade:   reply.Trade,
		Branch:  platform.Branch,
		Create:  now,
		Status:  reply.Status,
		Amount:  payment.Val,
		Receive: reply.Receive,
	}
	if reply.Currency != "" {
		data.Currency = reply.Currency
	} else {
		data.Currency = currency.Key
	}
	if tx = db.Create(data); tx.Error != nil {
		return tx.Error
	}
	return reply
}

// Refresh 查询订单最新状态,仅仅查询
// id string 游戏服订单ID
// trade string 平台订单ID,仅内购模式需要
// token string 平台订单核销TOKEN，,仅内购模式需要
//
//	trade 在第一次 Refresh 时会和 订单绑定，后续可以不传
func (this *Order) Refresh(c *context.Context) any {
	id := c.GetString("id")
	if id == "" {
		return errors.ErrArgEmpty
	}
	tx := db.Where("uid = ?", c.Uid())
	order := &model.Order{}
	if tx = tx.Find(order, id); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected == 0 {
		return errors.ErrDataNotExists
	} else if order.Uid != c.Uid() {
		return errors.ErrDataNotExists
	}
	if order.Status != model.OrderStatusCREATE {
		return &OrderReply{
			Id:       order.OID,
			Trade:    order.Trade,
			Status:   order.Status,
			Receive:  order.Receive,
			Currency: order.Currency,
		}
	}
	if r, err := this.refreshOrder(c, order); err != nil {
		return err
	} else {
		return r
	}
}

// Submit 发货并核销订单
// id string 订单唯一ID
// trade string 平台订单ID,仅内购模式需要
// token string 平台订单核销TOKEN，,仅内购模式需要
//
//	trade 在第一次 Refresh 时会和 订单绑定，如果已经使用 trade刷新过 后续可以不传
func (this *Order) Submit(c *context.Context) interface{} {
	id := c.GetString("id")
	if id == "" {
		return errors.ErrArgEmpty
	}
	tx := db.Where("uid = ?", c.Uid())
	order := &model.Order{}
	if tx = tx.Find(order, id); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected == 0 {
		return errors.ErrDataNotExists
	} else if order.Uid != c.Uid() {
		return errors.ErrDataNotExists
	}

	switch order.Status {
	case model.OrderStatusNone:
		return c.Errorf(0, "order status is none")
	case model.OrderStatusCREATE:
		if _, err := this.refreshOrder(c, order); err != nil {
			return err
		} else if order.Status != model.OrderStatusPAYMENT {
			return c.Errorf(0, "order status is error:%d", order.Status) //刷新仍然无法获取
		}
	case model.OrderStatusCANCEL:
		return c.Errorf(0, "order status is cancel")
	case model.OrderStatusSUCCESS:
		return c.Errorf(0, "order status is success")
	}

	orderConfig := config.Data.Order[order.IID]
	if orderConfig == nil {
		return values.Errorf(0, "config not exist") //配置被删，请找管理员
	}

	orderColl := cache.GetItems(c.Player.Updater)
	//首充
	if v := orderColl.Get(orderConfig.Id); v == nil || v.Value == 0 {
		c.Player.AddItems(orderConfig.First)
	}
	//常规道具发放
	c.Player.AddItems(orderConfig.Items)

	//核销订单
	reply, err := this.submitOrder(c, order)
	if err != nil {
		return err
	}
	//保存订单
	if tx = db.Select("status", "update", "receive", "orderid", "currency").Update(order, order.OID); tx.Error != nil {
		return tx.Error
	}

	orderColl.Add(orderConfig.Id, 1)
	if orderConfig.Value > 0 {
		c.Player.Emit(config.Data.Events.OrderValue, orderConfig.Value)
	}
	c.Player.Emit(config.Data.Events.OrderSubmit, orderConfig.Id)
	return reply
}

// Search 查找最近待支付或者待发货的订单
// iid int 配置ID
func (this *Order) Search(c *context.Context) any {
	if order, err := this.findOrder(c); err != nil {
		return err
	} else {
		return order
	}
}

// Payment 查询所有已经支付，未发货的订单，用于补单
// todo 后台已经支付，游戏服未支付,批量查询
func (this *Order) Payment(c *context.Context) any {
	var rows []*OrderReply
	tx := db.Model(&model.Order{}).Where("uid = ?", c.Uid())
	tx = tx.Where("status = ?", model.OrderStatusPAYMENT)
	if tx = tx.Find(&rows); tx.Error != nil {
		return tx.Error
	}
	if len(rows) > 0 {
		return rows
	}
	//查询新订单
	var orders []*model.Order
	tx = db.Model(&model.Order{}).Where("uid = ?", c.Uid())
	tx = tx.Where("status = ?", model.OrderStatusCREATE)
	tx = tx.Order("create", -1)
	if tx = tx.First(&orders); tx.Error != nil {
		return tx.Error
	} else if tx.RowsAffected == 0 {
		return rows
	}

	r, err := this.refreshOrder(c, orders[0])
	if err != nil {
		return err
	} else {
		rows = append(rows, r)
	}
	return rows
}

// Restore 重新激活订单，仅限于已经存在
func (this *Order) restoreOrder(c *context.Context, data *model.Order, branch, openid string) (*OrderReply, error) {
	args := map[string]interface{}{}
	args["id"] = data.OID
	args["openid"] = openid
	args["branch"] = branch
	args["client_ip"] = c.Player.ClientIp
	orderConfig := config.Data.Order[data.IID]
	if orderConfig == nil {
		return nil, errors.ErrConfigEmpty
	}
	args["subject"] = orderConfig.Name

	//args["token"] = c.GetString("token")
	reply := &OrderReply{}
	if err := options.Master.Post(options.MasterApiTypeOrderRestore, args, reply); err != nil {
		return nil, err
	}
	if err := this.updateOrder(c, data, reply); err != nil {
		return nil, err
	}

	return reply, nil
}

func (this *Order) refreshOrder(c *context.Context, data *model.Order) (*OrderReply, error) {
	args := map[string]interface{}{}
	args["id"] = data.OID
	args["trade"] = c.GetString("trade")
	args["token"] = c.GetString("token")
	//args["token"] = c.GetString("token")
	reply := &OrderReply{}
	if err := options.Master.Post(options.MasterApiTypeOrderRefresh, args, reply); err != nil {
		return nil, err
	}
	if err := this.updateOrder(c, data, reply); err != nil {
		return nil, err
	}
	return reply, nil
}

// updateOrder 需要更新订单状态
func (this *Order) updateOrder(c *context.Context, data *model.Order, reply *OrderReply) error {
	up := update.Update{}
	if data.Status != reply.Status {
		data.Status = reply.Status
		up.Set("status", reply.Status)
	}
	if reply.Receive > 0 && reply.Receive != data.Receive {
		up.Set("receive", reply.Receive)
		data.Receive = reply.Receive
	}
	if reply.Trade != "" && reply.Trade != data.Trade {
		up.Set("trade", reply.Trade)
		data.Trade = reply.Trade
	}
	if reply.Currency != "" && reply.Currency != data.Currency {
		up.Set("currency", reply.Currency)
		data.Currency = reply.Currency
	}

	if len(up) > 0 {
		up.Set("update", c.Unix())
		if tx := db.Model(data).Update(up, data.OID); tx.Error != nil {
			return tx.Error
		}
	}
	return nil
}

func (this *Order) submitOrder(c *context.Context, data *model.Order) (*OrderReply, error) {
	args := map[string]interface{}{}
	args["id"] = data.OID
	args["trade"] = c.GetString("trade")
	args["token"] = c.GetString("token")
	//args["token"] = c.GetString("token")
	reply := &OrderReply{}
	if err := options.Master.Post(options.MasterApiTypeOrderSubmit, args, reply); err != nil {
		return nil, err
	}
	if reply.Status != model.OrderStatusSUCCESS {
		return nil, c.Error("order verify status not success")
	}
	//修改订单信息
	data.Status = model.OrderStatusSUCCESS
	if reply.Trade != "" {
		data.Trade = reply.Trade
	}
	if reply.Receive > 0 {
		data.Receive = reply.Receive
	}
	if reply.Currency != "" {
		data.Currency = reply.Currency
	}
	return reply, nil
}

// findOrder 查找最近未完成的订单
func (this *Order) findOrder(c *context.Context) ([]*OrderReply, error) {
	iid := c.GetInt32("iid") //订单配置ID
	if iid <= 0 {
		return nil, errors.ErrArgEmpty
	}
	mod := model.Order{}
	var order []*OrderReply
	tx := db.Table(mod.TableName()).Where("uid = ?", c.Uid())
	tx = tx.Where("iid = ?", iid)
	tx = tx.Where("status IN ?", []model.OrderStatus{model.OrderStatusCREATE, model.OrderStatusPAYMENT})
	tx = tx.Order("update", -1)

	if tx = tx.Find(&order); tx.Error != nil {
		return nil, tx.Error
	}
	return order, nil
}
